package structure;

/**
 * Implementiert Interrupts.
 * @author Dorothee Kern und Patrick Lehmann
 */
public class Interrupt {

    private boolean TMR0Carry = false; //Wird bei Ueberlauf des TMR0-Registers gesetzt
    private int marker = 1; //Hilfe für den Prescalor, vergroeßert das Intervall, in dem in das Register geschrieben wird
    int counter = 0;
    private boolean oldRA4 = false;
    private boolean edgeOldRB0 = false; // merkt sich den alten Wert am RB0 Pin (Hilfe zur Flankenerkennung)
    //Merken der alten Werte an den Ports RB4-7
    private boolean oldRB4 = false;
    private boolean oldRB5 = false;
    private boolean oldRB6 = false;
    private boolean oldRB7 = false;

    /**
     * Konstruktor belegt die Attribute oldRB4-7 mit dem aktuellen Signal an diesen Pins
     */
    public Interrupt() {
        oldRB4 = Register.getInstance().getRegisterBit(0x6, 4);
        oldRB5 = Register.getInstance().getRegisterBit(0x6, 5);
        oldRB6 = Register.getInstance().getRegisterBit(0x6, 6);
        oldRB7 = Register.getInstance().getRegisterBit(0x6, 7);
    }

    /**
     * Wird ausgeführt bei ausglöstem Interrupt.
     */
    public void triggeredInterrupt() {
        //GIE global interrupt enable pruefen
        if (Register.getInstance().getRegisterBit(0x0b, 7) == true) {
            //push aktuellen PC auf den Stack
            PICStack.getInstance().push(Register.getInstance().getProgramCounter());
            //Springe an Interrupt-Adressbereich
            Register.getInstance().setProgramCounter(4);
        }
    }

    /**
     * Prueft ob ein Interrupt vorliegt.
     */
    public void checkInterrupt() {


        checkTMR0();

        if (Register.getInstance().getRegisterBit(0x0b, 4) == true) {
            checkIntRB0();
        }

        if (Register.getInstance().getRegisterBit(0x0b, 3) == true) {
            checkIntRBIE();
        }
    }

    /**
     * Die Methode prueft und setzt das TMR0-Register.
     */
    public void checkTMR0() {
        //Verhindet das Hochzählen des TMR0 um 2 Zyklen, wenn das Register vorbelegt ist.
        if (Register.getInstance().isTMR0preWritten() == true) {
            counter = 2;
        }

        counter--;
        if (counter <= 0) {
            //pruefen ob TMR0 an RA4 aktiv ist, oder Internal Instruction Cycle
            if (Register.getInstance().getRegisterBit(0x81, 5) == false) {
                //internal instruction cycle wird im TMR0 hochgezaehlt
                increaseTMR0noPrescaler();
            } else //pruefen ob beim Low-to-High gewechselt werden soll
            if (Register.getInstance().getRegisterBit(0x81, 4) == false) {
                if (oldRA4 == false && Register.getInstance().getRegisterBit(0x5, 4) == true) {
                    oldRA4 = true;
                    increaseTMR0();
                } else {
                    oldRA4 = Register.getInstance().getRegisterBit(0x5, 4);
                }
            } else {
                if (oldRA4 == true && Register.getInstance().getRegisterBit(0x5, 4) == false) {
                    oldRA4 = false;
                    increaseTMR0();
                } else {
                    oldRA4 = Register.getInstance().getRegisterBit(0x5, 4);
                }
            }
        }
    }

    /**
     * Erhoeht den TRMR0 unter Beruecksichtigung des Prescalers.
     * Zaehlt Flanken am RA4.
     */
    public void increaseTMR0() {
        //lokale Variablen
        int TMR0 = 0;
        int prescaler;
        int limit;
        //pruefen ob ein Ueberlauf stattfand
        if (TMR0Carry == false) {
            //Den Wert aus dem TMR0-Register in die lokale Variable speichern
            TMR0 = Register.getInstance().getRegisterByteAsInt(0x1);
            //prüfen ob Prescaler dem TMR0 gehört
            if (Register.getInstance().getRegisterBit(0x81, 3) == false) {
                //den Wert des Prescalers (Bit0-2) in die lokale Variable schreiben
                prescaler = Register.getInstance().getRegisterByteAsInt(0x81) & 0x7;
                //Das Limit für den Marker festlegen
                //Bsp: bei Prescalor 000 wird das Intervall verdoppelt -> Limit = 2
                limit = (int) Math.pow(2, prescaler) * 2;
            } else {
                limit = 1;
            }

            //pruefen on das Limit des Markers erreicht ist
            if (limit == marker) {
                //Da Limit erreicht: Marker zuruecksetzen
                marker = 1;
                //TMR0 erhöhen und ins Register zurueckschreiben
                TMR0++;
                Register.getInstance().setRegisterByteAsInt(0x1, TMR0);
            } else //Limit nicht erreicht -> nur Marker herhoehen
            {
                marker++;
            }

        } //Ueberlauf ist eingetroffen -> Interrupt ausloesen
        else {
            Register.getInstance().setRegisterByteAsInt(0x1, 0);
            TMR0Carry = false;
            //setzen des T0IF da Überlauf
            Register.getInstance().setRegisterBit(0x0B, 2, true);
            //Interrupt ausloesen wenn das TMR0-Int Bit gesetzt ist
            if (Register.getInstance().getRegisterBit(0x0b, 5)) {
                triggeredInterrupt();
            }



        }

        //Ueberpruefen ob das TMR0Carry gesetzt werden muss
        if (TMR0 == 0xFF) {
            TMR0Carry = true;
        }

    }

    /**
     * Erhoeht den TMR0 ohne Prescaler zu Beruecksichtigen;
     * Zaehlt die Instruction Cycles hoch.
     */
    public void increaseTMR0noPrescaler() {
        //lokale Variablen
        int TMR0 = 0;
        //pruefen ob ein Ueberlauf stattfand
        if (TMR0Carry == false) {
            //Den Wert aus dem TMR0-Register in die lokale Variable speichern
            TMR0 = Register.getInstance().getRegisterByteAsInt(0x1);
            //TMR0 erhöhen und ins Register zurueckschreiben
            TMR0++;
            Register.getInstance().setRegisterByteAsInt(0x1, TMR0);
        } //Ueberlauf ist eingetroffen -> Interrupt ausloesen
        else {
            Register.getInstance().setRegisterByteAsInt(0x1, 0);
            TMR0Carry = false;
            //setzen T0IF da Überlauf
            Register.getInstance().setRegisterBit(0x0B, 2, true);
            //Interrupt ausloesen wenn das TMR0-Int Bit gesetzt ist
            if (Register.getInstance().getRegisterBit(0x0b, 5)) {
                triggeredInterrupt();
            }
        }

        //Ueberpruefen ob das TMR0Carry gesetzt werden muss
        if (TMR0 == 0xFF) {
            TMR0Carry = true;
        }
    }

    /**
     * Prüfen ob am RB0 ein Interrupt vorliegt.
     * Beachten von steigenden und fallenden Flanken, anhand des INTEDG-Bits.
     *
     */
    public void checkIntRB0() {
        //prüfen des INTEDG-Bit ob auf steigende Flanke getriggert wird; True = steigende Flanke
        if (Register.getInstance().getRegisterBit(0x81, 6) == true) {
            //prüft ob ein Wechsel stattgefunden hat von false zu true -> steigende Flanke
            if (edgeOldRB0 == false && Register.getInstance().getRegisterBit(0x06, 0) == true) {
                edgeOldRB0 = true;
                //setzen des INTF-Bits
                Register.getInstance().getRegisterBit(0x0b, 1);
                //INTF setzen
                Register.getInstance().setRegisterBit(0x0b, 1, true);
                //Interrupt auslösen
                triggeredInterrupt();
            } //die Variable edgeOldRB0 wird mit dem aktuellen RB0 Pegel belegt;
            //dient zum Vergleich beim nächsten Aufruf
            else {
                edgeOldRB0 = Register.getInstance().getRegisterBit(0x06, 0);
            }
        } else if (edgeOldRB0 == true && Register.getInstance().getRegisterBit(0x06, 0) == false) {
            edgeOldRB0 = false;
            //setzen des INTF-Bits
            Register.getInstance().getRegisterBit(0x0b, 1);
            //INTF setzen
            Register.getInstance().setRegisterBit(0x0b, 1, true);
            //Interrupt auslösen
            triggeredInterrupt();
        } //die Variable edgeOldRB0 wird mit dem aktuellen RB0 Pegel belegt;
        //dient zum Vergleich beim nächsten Aufruf
        else {
            edgeOldRB0 = Register.getInstance().getRegisterBit(0x06, 0);
        }
    }

    /**
     * Interrupt an den Ports RB 4-7 überprüfen.
     */
    public void checkIntRBIE() {
        if (Register.getInstance().getRegisterBit(0x0b, 3) == true) {
            if (Register.getInstance().getRegisterBit(0x6, 4) != oldRB4) {
                //neuen Wert in oldRBx schreiben
                oldRB4 = Register.getInstance().getRegisterBit(0x6, 4);
                //RBIF Bit setzen
                Register.getInstance().setRegisterBit(0x0b, 0, true);
                //Interrupt auslösen
                triggeredInterrupt();
            }
        
            if (Register.getInstance().getRegisterBit(0x6, 5) != oldRB5) {
                oldRB5 = Register.getInstance().getRegisterBit(0x6, 5);
                Register.getInstance().setRegisterBit(0x0b, 0, true);
                triggeredInterrupt();
            }
            if (Register.getInstance().getRegisterBit(0x6, 6) != oldRB6) {
                oldRB6 = Register.getInstance().getRegisterBit(0x6, 6);
                Register.getInstance().setRegisterBit(0x0b, 0, true);
                triggeredInterrupt();
            }
            if (Register.getInstance().getRegisterBit(0x6, 7) != oldRB7) {
                oldRB7 = Register.getInstance().getRegisterBit(0x6, 7);
                Register.getInstance().setRegisterBit(0x0b, 0, true);
                triggeredInterrupt();
            }
        }
    }
}
